﻿using System;
using System.Messaging;

namespace Vision.Core.Components
{
    /// <summary>
    /// 
    /// </summary>
    public class ActivityQueueSender : IDisposable
    {
        private MessageQueue MessageQueue { get; set; }
                                
        /// <summary>
        /// 
        /// </summary>
        public ActivityQueueSender(string queuePath)
        {
            this.MessageQueue = new MessageQueue(queuePath);
            this.MessageQueue.Formatter = new BinaryMessageFormatter();
            this.MessageQueue.UseJournalQueue = true;            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="aqMessage"></param>
        public void SendVisionActivityMessage(Entities.Activities.VisionActivityMessage eaMessage)
        {            
            Message queueMessage = new Message();
            queueMessage.Formatter = new BinaryMessageFormatter();
            queueMessage.Label = "System Activity";
            queueMessage.Body = eaMessage;            
            this.MessageQueue.Send(queueMessage);            
        }
        
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            this.MessageQueue.Close();
            this.MessageQueue.Dispose();
        }        
    }

    /// <summary>
    /// 
    /// </summary>
    public class ActivityQueueListener : IDisposable
    {
        public delegate void MessageReceivedEventHandler(object sender, MessageEventArgs args);
        public event MessageReceivedEventHandler MessageReceived;

        private bool _listen;
        private MessageQueue _queue;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="queuePath"></param>
        public ActivityQueueListener(string queuePath)
        {
            _queue = new MessageQueue(queuePath);
            _queue.Formatter = new BinaryMessageFormatter();
            _queue.UseJournalQueue = true;
        }

        /// <summary>
        /// 
        /// </summary>
        public void Start()
        {
            _listen = true;
            _queue.PeekCompleted += new PeekCompletedEventHandler(OnPeekCompleted);
            _queue.ReceiveCompleted += new ReceiveCompletedEventHandler(OnReceiveCompleted);
            StartListening();
        }

        /// <summary>
        /// 
        /// </summary>
        public void Stop()
        {
            _listen = false;
            _queue.PeekCompleted -= new PeekCompletedEventHandler(OnPeekCompleted);
            _queue.ReceiveCompleted -= new ReceiveCompletedEventHandler(OnReceiveCompleted);
        }

        /// <summary>
        /// 
        /// </summary>
        private void StartListening()
        {
            if (!_listen){
                return;
            }

            // The MSMQ class does not have a BeginRecieve method that can take in a 
            // MSMQ transaction object. This is a workaround - we do a BeginPeek and then 
            // recieve the message synchronously in a transaction.
            // Check documentation for more details
            if (_queue.Transactional)
            {
                _queue.BeginPeek();
            }
            else
            {
                _queue.BeginReceive();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnPeekCompleted(object sender, PeekCompletedEventArgs e)
        {
            _queue.EndPeek(e.AsyncResult);
            MessageQueueTransaction trans = new MessageQueueTransaction();
            Message msg = null;
            try
            {
                trans.Begin();
                msg = _queue.Receive(trans);
                trans.Commit();
                StartListening();
                FireRecieveEvent(msg.Body);
            }
            catch
            {
                trans.Abort();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="body"></param>
        private void FireRecieveEvent(object body)
        {
            if (MessageReceived != null){
                MessageReceived(this, new MessageEventArgs(body));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
        {
            Message msg = _queue.EndReceive(e.AsyncResult);
            StartListening();
            FireRecieveEvent(msg.Body);
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (this._listen){
                this.Stop();
            }

            if (this._queue != null){
                this._queue.Close();
                this._queue.Dispose();
            }
        }                

        /// <summary>
        /// 
        /// </summary>
        public class MessageEventArgs : EventArgs
        {
            private object _messageBody;

            /// <summary>
            /// 
            /// </summary>
            public object MessageBody
            {
                get { return _messageBody; }
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="body"></param>
            public MessageEventArgs(object body)
            {
                _messageBody = body;
            }
        }        
    }        
}
